package services

import (
	"bufio"
	"context"
	"crypto/md5"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"io"
	"log"
	"mime/multipart"
	"net/http"
	"path/filepath"
	"strconv"
	"sync"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/zmqge/vireo-gin-admin/app/admin/models"
	"github.com/zmqge/vireo-gin-admin/app/admin/repositories"
	"github.com/zmqge/vireo-gin-admin/config"
	"github.com/zmqge/vireo-gin-admin/pkg/response"
	"github.com/zmqge/vireo-gin-admin/utils"
	"golang.org/x/sync/semaphore"
)

// FileService 文件服务接口
type FileService interface {
	UploadFile(ctx *gin.Context, fileHeader *multipart.FileHeader) (gin.H, error)
	GetFileByID(ctx *gin.Context, id uint) (*models.FileModel, error)
	DeleteFile(ctx *gin.Context, id uint) error
	PageFiles(ctx *gin.Context, keywords string, pageNum, pageSize int) ([]*models.FileDto, int64, error)
}

// fileServiceImpl 文件服务实现
type fileServiceImpl struct {
	repo repositories.FileRepository
}

// NewFileService 创建文件服务
func NewFileService(repo repositories.FileRepository) FileService {
	return &fileServiceImpl{repo: repo}
}

// 全局通道池，减少通道创建开销
var resultChanPool = sync.Pool{
	New: func() interface{} {
		return make(chan struct {
			data gin.H
			err  error
		}, 1)
	},
}

// 全局信号量，限制最大并发处理数

// 上传文件
func (s *fileServiceImpl) UploadFile(ctx *gin.Context, fileHeader *multipart.FileHeader) (gin.H, error) {
	// 从上下文中获取用户ID
	userIDString := ctx.GetString("userID")
	if userIDString == "" {
		return nil, fmt.Errorf("用户ID不存在")
	}
	userID, err := strconv.ParseUint(userIDString, 10, 64)
	if err != nil {
		return nil, fmt.Errorf("用户ID格式错误")
	}

	var processSemaphore = semaphore.NewWeighted(config.App.FileUpload.MaxConcurrent) // 最大100个并发任务

	log.Printf("开始处理文件上传", "filename", fileHeader.Filename, "size", fileHeader.Size)

	// 1. 验证文件大小(不需要读取文件内容)
	maxSizeMB := config.App.FileUpload.MaxSizeMB
	if fileHeader.Size > int64(maxSizeMB<<20) {
		log.Printf("文件大小超过限制", "size", fileHeader.Size, "maxSize", maxSizeMB<<20)
		return nil, fmt.Errorf("文件大小超过限制(%dMB)", maxSizeMB)
	}

	// 2. 打开文件
	file, err := fileHeader.Open()
	if err != nil {
		log.Printf("打开文件失败", "error", err)
		return nil, err
	}
	defer file.Close()

	// 3. 从通道池获取通道
	resultChan := resultChanPool.Get().(chan struct {
		data gin.H
		err  error
	})
	defer resultChanPool.Put(resultChan)

	// 4. 尝试获取处理许可
	timeoutCtx, cancel := context.WithTimeout(ctx.Request.Context(), 2*time.Minute)
	defer cancel()

	if err := processSemaphore.Acquire(timeoutCtx, 1); err != nil {
		response.Error(ctx, fmt.Errorf("系统繁忙，请稍后重试: %w", err))
		return nil, err
	}
	defer processSemaphore.Release(1)

	// 5. 创建单独的处理上下文(与请求超时分离)
	processCtx, processCancel := context.WithTimeout(timeoutCtx, 90*time.Second)
	defer processCancel()

	// 6. 异步处理文件
	go func() {
		defer func() {
			if r := recover(); r != nil {
				resultChan <- struct {
					data gin.H
					err  error
				}{nil, fmt.Errorf("处理panic: %v", r)}
			}
		}()

		// 6.1 检测MIME类型
		fileType, err := detectMimeType(processCtx, file)
		if err != nil {
			resultChan <- struct {
				data gin.H
				err  error
			}{nil, err}
			return
		}

		// 6.2 重置文件指针
		if _, err := file.Seek(0, io.SeekStart); err != nil {
			resultChan <- struct {
				data gin.H
				err  error
			}{nil, err}
			return
		}

		// 6.3 计算哈希
		md5Hash, sha256Hash, err := calculateHashes(processCtx, file)
		if err != nil {
			resultChan <- struct {
				data gin.H
				err  error
			}{nil, err}
			return
		}

		// 6.4 重置文件指针
		if _, err := file.Seek(0, io.SeekStart); err != nil {
			resultChan <- struct {
				data gin.H
				err  error
			}{nil, err}
			return
		}

		// 6.5 保存文件到存储
		fileModel := &models.FileModel{
			Name:        fileHeader.Filename,
			Size:        fileHeader.Size,
			MimeType:    fileType,
			Ext:         filepath.Ext(fileHeader.Filename),
			StorageType: config.App.FileUpload.StorageType,
			Md5:         md5Hash,
			Sha256:      sha256Hash,
			SavePath:    "",
		}
		fileBySHA256, err := s.repo.GetFileBySHA256(processCtx, sha256Hash, fileModel.StorageType)
		log.Printf("查询文件结果", "fileBySHA256", fileBySHA256)
		var storagePath string
		if fileBySHA256 == nil {
			storagePath, err = s.repo.SaveToStorage(processCtx, fileModel, file)
			if err != nil {
				// 修正1: 正确记录错误日志
				err := fmt.Errorf("文件存储失败: %w", err)
				log.Printf("文件存储失败: %v", err)
				resultChan <- struct {
					data gin.H
					err  error
				}{nil, err}
				return // 关键修正: 出错后直接返回，不再继续执行
			}
		} else {
			storagePath = fileBySHA256.SavePath
		}
		fileModel.SavePath = storagePath
		// 6.6 保存元数据
		//如果文件已经存在，则判断是否属于当前用户，如果属于则不添加新纪录
		//如果文件已经存在，则判断是否属于当前用户，如果不属于则添加新纪录
		if fileBySHA256.CreatorID != 0 {
			if *&fileBySHA256.CreatorID == uint(userID) {
				resultChan <- struct {
					data gin.H
					err  error
				}{
					data: gin.H{
						"id":           fileBySHA256.ID,
						"md5":          md5Hash,
						"sha256":       sha256Hash,
						"size":         fileHeader.Size,
						"content_type": fileType,
						"save_path":    storagePath,
						"url":          utils.BuildFileURL(storagePath),
						"filename":     fileHeader.Filename,
						"ext":          filepath.Ext(fileHeader.Filename),
						"storage_type": fileModel.StorageType,
					},
					err: nil,
				}
				return
			}
		}

		if err := s.repo.CreateFile(ctx, fileModel); err != nil {
			log.Printf("保存文件元数据失败", "error", err, "fileModel", fileModel)
			resultChan <- struct {
				data gin.H
				err  error
			}{nil, err}
			return
		}
		log.Printf("文件上传成功", "fileID", fileModel.ID, "storagePath", storagePath)
		url := utils.BuildFileURL(storagePath)
		// 6.7 返回结果
		resultChan <- struct {
			data gin.H
			err  error
		}{
			data: gin.H{
				"id":           fileModel.ID,
				"md5":          md5Hash,
				"sha256":       sha256Hash,
				"size":         fileHeader.Size,
				"content_type": fileType,
				"save_path":    storagePath,
				"url":          url,
				"filename":     fileHeader.Filename,
				"ext":          filepath.Ext(fileHeader.Filename),
				"storage_type": fileModel.StorageType,
			},
			err: nil,
		}
	}()

	// 7. 等待处理结果
	select {
	case result := <-resultChan:
		return result.data, result.err
	case <-ctx.Done():
		return nil, ctx.Err()
	}
}

func (s *fileServiceImpl) GetFileByID(ctx *gin.Context, id uint) (*models.FileModel, error) {
	return s.repo.GetFileByID(ctx, id)
}

func (s *fileServiceImpl) DeleteFile(ctx *gin.Context, id uint) error {
	return s.repo.DeleteFile(ctx, id)
}

func (s *fileServiceImpl) PageFiles(ctx *gin.Context, keywords string, pageNum, pageSize int) ([]*models.FileDto, int64, error) {
	return s.repo.PageFiles(ctx, keywords, pageNum, pageSize)
}

// 流式检测MIME类型（添加上下文支持）
func detectMimeType(ctx context.Context, file multipart.File) (string, error) {
	buffer := make([]byte, 512)

	// 添加超时控制
	ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
	defer cancel()

	// 使用带上下文的读取
	n, err := io.ReadAtLeast(file, buffer, 512)
	if err != nil && err != io.ErrUnexpectedEOF {
		return "", err
	}

	return http.DetectContentType(buffer[:n]), nil
}

// 流式计算哈希（添加上下文支持）
func calculateHashes(ctx context.Context, file multipart.File) (string, string, error) {
	hashMD5 := md5.New()
	hashSHA256 := sha256.New()
	multiWriter := io.MultiWriter(hashMD5, hashSHA256)

	// 使用bufio.Reader的缓冲读取
	bufReader := bufio.NewReaderSize(file, 8*1024*1024) // 8MB缓冲区

	// 创建一个缓冲区
	buf := make([]byte, 32*1024) // 32KB缓冲区

	for {
		// 检查上下文是否已取消
		select {
		case <-ctx.Done():
			return "", "", ctx.Err()
		default:
			// 上下文未取消，继续读取
		}

		// 从文件读取数据
		n, err := bufReader.Read(buf)
		if n > 0 {
			// 将读取的数据写入哈希计算器
			if _, err = multiWriter.Write(buf[:n]); err != nil {
				return "", "", err
			}
		}
		if err != nil {
			if err == io.EOF {
				// 正常结束
				return hex.EncodeToString(hashMD5.Sum(nil)),
					hex.EncodeToString(hashSHA256.Sum(nil)),
					nil
			}
			// 其他错误
			return "", "", err
		}
	}
}
